using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using HCP.Responses;
using HCP.SimpleJSON;
using UnityEngine;

namespace HCP
{
	public class Server : MonoBehaviour
	{
		public HttpListener Listener
		{
			get
			{
				return this.m_listener;
			}
		}

		public string ListenerURI
		{
			get
			{
				return this.m_sListenerURI;
			}
		}

		public bool AcceptConnections
		{
			get
			{
				return this.Listener != null;
			}
		}

		public static float DeviceScreenScalar
		{
			get
			{
				return 1f;
			}
		}

		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private event ListenerStartedEventHandler Started;

		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private event LIstenerStoppedEventHandler Stopped;

		private void WaitContext()
		{
			ManualResetEvent httpClientConnected = new ManualResetEvent(false);
			this.Listener.BeginGetContext(delegate(IAsyncResult result)
			{
				try
				{
					if (this.Listener != null)
					{
						HttpListenerContext context = this.Listener.EndGetContext(result);
						this.AcceptContext(context);
					}
				}
				catch (ObjectDisposedException)
				{
				}
				finally
				{
					httpClientConnected.Set();
				}
			}, null);
			httpClientConnected.WaitOne();
		}

		private void AcceptContext(HttpListenerContext context)
		{
			HttpListenerRequest request = context.Request;
			HttpListenerResponse response = context.Response;
			string s = new ErrorResponse().ToJSON(0, null).ToString();
			if (request.RawUrl.StartsWith("/alive"))
			{
				s = "Appium-HCP Socket Server Ready";
			}
			else if (request.RawUrl.StartsWith("/action"))
			{
				string task;
				using (StreamReader streamReader = new StreamReader(request.InputStream, request.ContentEncoding))
				{
					task = streamReader.ReadToEnd();
				}
				Job job = this.QueueActionRequest(task);
				job.Await();
				s = job.Response.ToJSON(0, null).ToString();
			}
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			Stream outputStream = response.OutputStream;
			response.SendChunked = true;
			for (int i = 0; i < bytes.Length; i += 1000)
			{
				outputStream.Write(bytes, i, Math.Min(1000, bytes.Length - i));
				outputStream.Flush();
			}
			outputStream.Close();
		}

		private void Transition(Server.EServerStateTransition transition)
		{
			if (this.m_stateTransition == transition)
			{
				return;
			}
			if (transition == Server.EServerStateTransition.STARTING && this.m_state == Server.EServerState.STOPPED)
			{
				this.m_stateTransition = Server.EServerStateTransition.STARTING;
				this.StartServer();
				this.m_state = Server.EServerState.STARTED;
				this.m_stateTransition = Server.EServerStateTransition.NONE;
			}
			else if (transition == Server.EServerStateTransition.STOPPING && this.m_state == Server.EServerState.STARTED)
			{
				this.m_stateTransition = Server.EServerStateTransition.STOPPING;
				this.StopServer();
				this.m_state = Server.EServerState.STOPPED;
				this.m_stateTransition = Server.EServerStateTransition.NONE;
			}
		}

		private void StartServer()
		{
			if (!HttpListener.IsSupported)
			{
				throw new InvalidOperationException("Server: The HttpListener class is unsupported!  I will not be able to provide Appium with data.");
			}
			this.m_listener = new HttpListener();
			this.m_listener.Prefixes.Add(this.ListenerURI + "/alive/");
			this.m_listener.Prefixes.Add(this.ListenerURI + "/action/");
			this.m_listener.Start();
			this.m_listenerThread = new Thread(delegate()
			{
				this.Run();
			});
			this.m_listenerThread.Start();
			if (this.Started != null)
			{
				this.Started();
			}
			UnityEngine.Debug.Log("Server: Started HCP Server");
		}

		private void StopServer()
		{
			if (this.Listener != null)
			{
				this.Listener.Stop();
				this.Listener.Close();
				this.m_listener = null;
			}
			if (this.m_listenerThread != null)
			{
				this.m_listenerThread.Join();
				this.m_listenerThread = null;
			}
			if (this.Stopped != null)
			{
				this.Stopped();
			}
			UnityEngine.Debug.Log("Server: Stopped HCP Server");
		}

		private void CloseServer()
		{
			UnityEngine.Debug.Log("Server: Closing HCP Server");
		}

		private void Run()
		{
			try
			{
				while (this.AcceptConnections)
				{
					this.WaitContext();
				}
			}
			catch (Exception exception)
			{
				UnityEngine.Debug.LogException(exception);
			}
			finally
			{
				this.Transition(Server.EServerStateTransition.STOPPING);
			}
		}

		private void AddActionHandler(string requestCommand, Type requestType)
		{
			if (requestType.IsSubclassOf(typeof(JobRequest)))
			{
				this.m_requestCommands.Add(requestCommand, requestType);
				this.requestCommands.Add(requestCommand);
				return;
			}
			throw new ArgumentException("Server: requestType must be of type JobRequest");
		}

		private Job QueueActionRequest(string task)
		{
			Job job = new Job();
			JSONNode jsonnode = JSON.Parse(task);
			string value = jsonnode["cmd"].Value;
			if (value == "action")
			{
				string value2 = jsonnode["action"].Value;
				JSONNode jsonnode2 = jsonnode["params"];
				if (this.m_requestCommands.ContainsKey(value2))
				{
					Type type = this.m_requestCommands[value2];
					job.Request = (JobRequest)Activator.CreateInstance(type, new object[]
					{
						jsonnode2
					});
				}
				else
				{
					UnityEngine.Debug.Log("Server: unhandled action: " + value2);
				}
				this.m_requestJobs.Enqueue(job);
				return job;
			}
			throw new ArgumentException("Server: Cannot queue an action of unknown command type: " + value);
		}

		private void Awake()
		{
			this.m_requestCommands = new Dictionary<string, Type>();
			this.AddActionHandlers();
			this.m_requestJobs = new Queue<Job>();
		}

		private void AddActionHandlers()
		{
			List<Type> typesInNameSpace = this.GetTypesInNameSpace(Assembly.GetExecutingAssembly(), "HCP.Requests");
			for (int i = 0; i < typesInNameSpace.Count; i++)
			{
				this.AddActionHandler(typesInNameSpace[i].Name, typesInNameSpace[i]);
			}
		}

		private List<Type> GetTypesInNameSpace(Assembly assembly, string nameSpace)
		{
			Type[] types = assembly.GetTypes();
			List<Type> list = new List<Type>();
			foreach (Type type in types)
			{
				if (type.Namespace == nameSpace && type.IsClass && type.BaseType == typeof(JobRequest) && !this.m_requestCommands.ContainsValue(type))
				{
					list.Add(type);
				}
			}
			return list;
		}

		private void Start()
		{
			//UnityEngine.Debug.Log("Server started. Listening on URI: " + Network.player.ipAddress + ":14813");
		}

		private void OnEnable()
		{
			this.Transition(Server.EServerStateTransition.STARTING);
		}

		private void OnDisable()
		{
			this.Transition(Server.EServerStateTransition.STOPPING);
		}

		private void OnDestroy()
		{
			this.CloseServer();
		}

		private void Update()
		{
			if (this.m_requestJobs.Count > 0)
			{
				Job job = this.m_requestJobs.Peek();
				try
				{
					UnityEngine.Debug.Log("Process." + job.Request.GetType().FullName);
					job.Process();
				}
				catch (Exception exception)
				{
					job.State = Job.EState.ERROR;
					UnityEngine.Debug.LogException(exception);
				}
				finally
				{
					if (job.IsComplete)
					{
						this.m_requestJobs.Dequeue();
						job.Dispose();
					}
				}
			}
		}

		[SerializeField]
		private string m_sListenerURI = "http://*:14813";

		private HttpListener m_listener;

		private Server.EServerState m_state;

		private Server.EServerStateTransition m_stateTransition;

		private Thread m_listenerThread;

		protected Dictionary<string, Type> m_requestCommands;

		protected Queue<Job> m_requestJobs;

		public List<string> requestCommands = new List<string>();

		private enum EServerState
		{
			STOPPED,
			STARTED
		}

		private enum EServerStateTransition
		{
			NONE,
			STOPPING,
			STARTING
		}
	}
}
